package com.zhengxing.jienci.user.service.rbac.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.zhengxing.jienci.user.domain.rbac.Resource;
import com.zhengxing.jienci.user.domain.rbac.RoleResourceFunc;
import com.zhengxing.jienci.common.util.IdGenerateUtil;
import com.zhengxing.jienci.user.mapper.rbac.RoleResourceFuncMapper;
import com.zhengxing.jienci.user.service.rbac.ResourceFuncService;
import com.zhengxing.jienci.user.service.rbac.ResourceService;
import com.zhengxing.jienci.user.service.rbac.RoleResourceFuncService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

import static com.zhengxing.jienci.user.service.rbac.impl.ResourceServiceImpl.REDIS_KEY_PREFIX_FIND_RESOURCES_BY_USER_ID;

/**
 * RoleResourceFuncServiceImpl
 *
 * @author philbert
 * @version 1.00
 */
@Slf4j
@Service
@Transactional
@Lazy
public class RoleResourceFuncServiceImpl extends ServiceImpl<RoleResourceFuncMapper, RoleResourceFunc> implements RoleResourceFuncService {

    @Autowired
    private RoleResourceFuncMapper roleResourceFuncMapper;
    @Autowired
    private ResourceService resourceService;
    @Autowired
    private ResourceFuncService resourceFuncService;
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 保存角色的权限
     *
     * @param roleId 角色主键
     * @param perms  权限
     */
    @Override
    public void savePerms(String roleId, String[] perms) {
        if (perms == null) perms = new String[0];
        List<RoleResourceFunc> roleResourceFuncs = new ArrayList<>();
        Set<String> resourceIdSet = new HashSet<>();
        for (String perm : perms) {
            String[] array = perm.split("\\|");
            RoleResourceFunc roleResourceFunc = new RoleResourceFunc();
            roleResourceFunc.setRoleId(roleId);
            roleResourceFunc.setResourceId(array[0]);
            roleResourceFunc.setResourceFuncId(array[1]);
            if (StringUtils.isBlank(roleResourceFunc.getId())) roleResourceFunc.setId(IdGenerateUtil.getId());
            if (roleResourceFunc.getCreatedAt() == null) roleResourceFunc.setCreatedAt(System.currentTimeMillis());
            roleResourceFuncs.add(roleResourceFunc);

            resourceIdSet.add(array[0]);
        }
        resourceIdSet.forEach(
                resourceId -> {
                    RoleResourceFunc roleResourceFunc = new RoleResourceFunc();
                    roleResourceFunc.setRoleId(roleId);
                    roleResourceFunc.setResourceId(resourceId);
                    roleResourceFunc.setResourceFuncId(resourceFuncService.getListFuncIdByResourceId(resourceId));
                    if (StringUtils.isBlank(roleResourceFunc.getId())) roleResourceFunc.setId(IdGenerateUtil.getId());
                    if (roleResourceFunc.getCreatedAt() == null)
                        roleResourceFunc.setCreatedAt(System.currentTimeMillis());
                    roleResourceFuncs.add(roleResourceFunc);
                }
        );
        resourceIdSet.forEach(resourceId -> genParentRoleResources(roleId, resourceId, roleResourceFuncs));

        this.deleteByRoleIds(Lists.newArrayList(roleId));
        if (!roleResourceFuncs.isEmpty()) super.saveBatch(roleResourceFuncs, 100);

        Set<String> keySet = redisTemplate.keys(REDIS_KEY_PREFIX_FIND_RESOURCES_BY_USER_ID + "*");
        if (keySet != null) redisTemplate.delete(keySet);
    }

    /**
     * 根据角色主键集合删除权限
     *
     * @param roleIds 角色主键集合
     */
    @Override
    public void deleteByRoleIds(Collection<String> roleIds) {
        LambdaUpdateWrapper<RoleResourceFunc> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        if (!roleIds.isEmpty()) {
            if (roleIds.size() == 1)
                roleIds.forEach(roleId -> lambdaUpdateWrapper.eq(RoleResourceFunc::getRoleId, roleId));
            else
                lambdaUpdateWrapper.in(RoleResourceFunc::getRoleId, roleIds);

            roleResourceFuncMapper.delete(lambdaUpdateWrapper);
        }
    }

    /**
     * 根据资源主键集合删除权限
     *
     * @param resourceIds 资源主键集合
     */
    @Override
    public void deleteByResourceIds(Collection<String> resourceIds) {
        LambdaUpdateWrapper<RoleResourceFunc> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        if (!resourceIds.isEmpty()) {
            if (resourceIds.size() == 1)
                resourceIds.forEach(resourceId -> lambdaUpdateWrapper.eq(RoleResourceFunc::getResourceId, resourceId));
            else
                lambdaUpdateWrapper.in(RoleResourceFunc::getResourceId, resourceIds);

            roleResourceFuncMapper.delete(lambdaUpdateWrapper);
        }
    }

    /**
     * 根据资源功能主键集合删除权限
     *
     * @param resourceFuncIds 资源功能主键集合
     */
    @Override
    public void deleteByResourceFuncIds(Collection<String> resourceFuncIds) {
        LambdaUpdateWrapper<RoleResourceFunc> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        if (!resourceFuncIds.isEmpty()) {
            if (resourceFuncIds.size() == 1)
                resourceFuncIds.forEach(resourceFuncId -> lambdaUpdateWrapper.eq(RoleResourceFunc::getResourceFuncId, resourceFuncId));
            else
                lambdaUpdateWrapper.in(RoleResourceFunc::getResourceFuncId, resourceFuncIds);

            roleResourceFuncMapper.delete(lambdaUpdateWrapper);
        }
    }

    /**
     * 通过角色主键查询该角色所有的资源
     *
     * @param roleId 角色主键
     * @return 该角色所有的资源
     */
    @Override
    @Transactional(readOnly = true)
    public List<RoleResourceFunc> findByRoleId(String roleId) {
        SFunction<RoleResourceFunc, String> sFunction = RoleResourceFunc::getRoleId;
        return findByProp(sFunction, roleId);
    }

    /**
     * 通过资源主键查询该资源所属的所有角色
     *
     * @param resourceId 资源主键
     * @return 该资源所属的所有角色
     */
    @Override
    @Transactional(readOnly = true)
    public List<RoleResourceFunc> findByResourceId(String resourceId) {
        SFunction<RoleResourceFunc, String> sFunction = RoleResourceFunc::getResourceId;
        return findByProp(sFunction, resourceId);
    }

    /**
     * 通过资源主键和操作（功能代码）删除角色资源
     *
     * @param resourceId 资源主键
     * @param oper       操作（功能代码）
     */
    public void deleteByResourceIdAndOper(String resourceId, String oper) {
        //
    }

    private List<RoleResourceFunc> findByProp(SFunction<RoleResourceFunc, String> sFunction, String propValue) {
        LambdaQueryWrapper<RoleResourceFunc> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(sFunction, propValue);
        return roleResourceFuncMapper.selectList(lambdaQueryWrapper);
    }

    /**
     * 递归生成上级资源的角色权限
     *
     * @param roleId            角色ID
     * @param resourceId        资源ID
     * @param roleResourceFuncs 角色权限List
     */
    private void genParentRoleResources(String roleId, String resourceId, List<RoleResourceFunc> roleResourceFuncs) {
        Resource resource = resourceService.get(resourceId);
        String parentId = resource.getParentId();
        if (!StringUtils.equalsIgnoreCase(parentId, "0")) {
            boolean exist = false;
            for (RoleResourceFunc roleResourceFunc : roleResourceFuncs) {
                if (StringUtils.equalsIgnoreCase(roleResourceFunc.getResourceId(), parentId)) {
                    exist = true;
                    break;
                }
            }
            if (!exist) {
                RoleResourceFunc roleResourceFunc = new RoleResourceFunc();
                roleResourceFunc.setRoleId(roleId);
                roleResourceFunc.setResourceId(parentId);
                roleResourceFunc.setResourceFuncId(resourceFuncService.getListFuncIdByResourceId(parentId));
                if (StringUtils.isBlank(roleResourceFunc.getId())) roleResourceFunc.setId(IdGenerateUtil.getId());
                if (roleResourceFunc.getCreatedAt() == null) roleResourceFunc.setCreatedAt(System.currentTimeMillis());
                roleResourceFuncs.add(roleResourceFunc);
            }

            genParentRoleResources(roleId, parentId, roleResourceFuncs);
        }
    }

}
